#!/usr/bin/env python
# -*-coding:utf-8 -*-

__all__ = ['Vector', 'getX', 'setX', 'getY', 'setY', 'getZ', 'setZ', 'update', 'norm', 
           '__add__', '__iadd__' ,'__sub__', '__isub__', '__eq__', '__mul__', '__imul__', 
           '__truediv__', '__itruediv__', 'copy', 'dot', '__hash__']

import numpy as np

class Vector(object):
    def __init__(self, x, y, z=0.0):
        """
        创建一个向量。
        :param x: 向量的x分量。
        :param y: 向量的y分量。
        :param z: 向量的z分量。默认为0。
        """
        self._x = 1.0*x
        self._y = 1.0*y
        self._z = 1.0*z

    def getX(self):
        """
        获取向量的x分量。
        :reurn: 向量的x分量。
        """
        return self._x

    def setX(self, x):
        """
        设置向量的x分量。
        :param x: 设置的向量的x分量。
        """
        self._x = x

    def getY(self):
        """
        获取向量的y分量。
        :reurn: 向量的y分量。
        """
        return self._y

    def setY(self, y):
        """
        设置向量的y分量。
        :param y: 设置的向量的y分量。
        """
        self._y = y

    def getZ(self):
        """
        获取向量的z分量。
        :reurn: 向量的z分量。
        """
        return self._z

    def setZ(self, z):
        """
        设置向量的z分量。
        :param z: 设置的向量的z分量。
        """
        self._z = z

    def update(self, x, y, z=0):
        """
        更新向量x, y, z分量的值。
        :param x: 更新的x分量。
        :param y: 更新的y分量。
        :param z: 更新的z分量。默认为0。
        """
        self._x = 1.0*x
        self._y = 1.0*y
        self._z = 1.0*z

    def norm(self):
        """
        获取向量的长度。
        :return: 向量的长度。
        """
        return np.sqrt((self._x)**2 + (self._y)**2 + (self._z)**2)

    def __add__(self, other):
        """
        两个向量相加。
        :param other: 与本向量相加的向量。
        :return: 一个新建的向量，为本向量与other之和。
        """
        return Vector(self._x+other.getX(), self._y+other.getY(), self._z+other.getZ())

    def __iadd__(self, other):
        """
        两个向量在位相加。
        :param other: 与本向量相加的向量。
        :return: 本向量，其值更新为本向量与other之和。
        """
        self._x += other.getX()
        self._y += other.getY()
        self._z += other.getZ()
        return self

    def __sub__(self, other):
        """
        两个向量相减。
        :param other: 本向量所要减去的向量。
        :return: 一个新建的向量，为本向量减去other。
        """
        return Vector(self._x-other.getX(), self._y-other.getY(), self._z-other.getZ())

    def __isub__(self, other):
        """
        两个向量在位相减。
        :param other: 本向量所要减去的向量。
        :return: 本向量，其值更新为本向量减去other。
        """
        self._x -= other.getX()
        self._y -= other.getY()
        self._z -= other.getZ()
        return self

    def __eq__(self, other):
        """
        判断两个向量是否相等。
        :param other: 判断是否与本向量相等的向量。
        :return: 如果相等，返回True，否则返回False。
        """
        if isinstance(other, Vector):
            return (self._x == other.getX() and self._y == other.getY() and self._z == other.getZ())
        else:
            return False

    def __mul__(self, scale):
        """
        向量乘以一个标量scale。
        :param scale: 与本向量相乘的标量。
        :return: 一个新建的向量，为本向量乘以标量scale。
        """
        return Vector(self._x*scale, self._y*scale, self._z*scale)

    def __imul__(self, scale):
        """
        向量乘以一个标量scale，在位相乘。
        :param scale: 与本向量相乘的标量。
        :return: 本向量，其值更新为本向量乘以标量scale。
        """
        self._x *= scale
        self._y *= scale
        self._z *= scale
        return self

    def __truediv__(self, scale):
        """
        向量除以一个标量scale。
        :param scale: 本向量所要除以的标量。
        :return: 一个新建的向量，其值为本向量除以标量scale。
        """
        return Vector(self._x/scale, self._y/scale, self._z/scale)

    def __itruediv__(self, scale):
        """
        向量除以一个标量scale。在位相除。
        :param scale: 本向量所要除以的标量。
        :return: 本向量，其值更新为本向量除以标量scale。
        """
        self._x /= scale
        self._y /= scale
        self._z /= scale
        return self

    def copy(self):
        """
        获取本向量的一个拷贝。
        :return: 本向量的一个拷贝。
        """
        return Vector(self._x, self._y, self._z)

    def dot(self, other):
        """
        两个向量点乘。
        :param other: 本向量与之点乘的向量。
        :return: 点乘的结果。
        """
        return self._x*other.getX() + self._y*other.getY() + self._z*other.getZ()
        
    def angleWith(self, other):
        """
        两个向量之间的夹角。
        :param other: 本向量与之求夹角的向量。
        :return: 夹角。
        """
        cos = self.dot(other)/(self.norm() * other.norm())
        if cos > 1:
            cos = 1
        elif cos < -1:
            cos = -1
        return 180.0 * np.arccos(cos) / np.pi
        
    def __hash__(self):
        """
        本向量的哈希。
        :return: 本向量的哈希。
        """
        return hash(self._x)^hash(self._y)^hash(self._z)


if __name__ == '__main__':
    vec = Vector(1, 2, 3)
    print('vec = ({0}, {1}, {2})'.format(vec.getX(), vec.getY(), vec.getZ()))
    print('its norm is {0}'.format(vec.norm()))

    if vec == Vector(1, 2, 3):
        print('vec equals Vector(1,2,3)!')
